﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using cpe.cpein.DATA;

namespace cpe.cpein.BUSINESS
{

    public class UserComparer : IEqualityComparer<User>
    {

        #region IEqualityComparer<User> Members

        public bool Equals(User x, User y)
        {
            return x.usr_id == y.usr_id;
        }

        public int GetHashCode(User obj)
        {
            return obj.usr_id.GetHashCode();
        }

        #endregion
    }

    public static class BUUsers
    {
        /// <summary>
        /// Get All the Users of the database.
        /// </summary>
        /// <param name="searchInDeleted"></param>
        /// <returns></returns>
        public static List<User> GetAllUsers(bool searchInDeleted)
        {
            List<User> users = new List<User>();
            using (cpeinEntities db = new cpeinEntities())
            {
                var results = from User in db.User
                              where User.usr_isdeleted == searchInDeleted
                              select User;
                foreach (User tmp in results)
                    users.Add(tmp);
            }

            return users;
        }

        /// <summary>
        /// Get the User by id.
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public static User GetUserByID(int id) 
        {
            if (id <= 0)
                return null;
            else
            {
                using (cpeinEntities db = new cpeinEntities())
                {
                    var user = from User in db.User
                               where User.usr_id == id
                               select User;
                    foreach (User tmp in user)
                        return tmp;
                    return null;
                }
            }
        }

        /// <summary>
        /// Add a User into the database.
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public static User AddUser(User user)
        {
            if (user != null)
            {
                using (cpeinEntities db = new cpeinEntities())
                {
                    bool exists = false;

                    var req = from User in GetAllUsers(false)
                              where User.usr_login == user.usr_login
                              select User;
                    exists = (req.ToList().Count() > 0);

                    if (!exists)
                    {
                        User nUser = db.User.Add(user);
                        db.SaveChanges();
                        return nUser;
                    }
                }
            }
            
            return null;
        }

        /// <summary>
        /// Remove a specific User of the database.
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public static bool RemoveUser(User user)
        {
            if (user != null)
            {
                user.usr_isdeleted = true;
                if (UpdateUser(user) != null)
                {
                    using (cpeinEntities db = new cpeinEntities())
                    {
                        db.SaveChanges();
                        return true;
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// Update the User into the database.
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public static User UpdateUser(User user)
        {
            if(user != null)
            {
                using (cpeinEntities db = new cpeinEntities())
                {
                    var current = from User in db.User
                                  where User.usr_id == user.usr_id
                                  select User;

                    foreach (var tmp in current)
                    {
                        tmp.usr_email = user.usr_email;
                        tmp.usr_isdeleted = user.usr_isdeleted;
                        tmp.usr_lastname = user.usr_lastname;
                        tmp.usr_login = user.usr_login;
                        tmp.usr_name = user.usr_name;
                        tmp.usr_password = user.usr_password;
                        tmp.usr_profil_img_url = user.usr_profil_img_url;
                    }

                    int num = db.SaveChanges();
                    Console.WriteLine("No conflicts. " +
                        num.ToString() + " updates saved.");
                    user = current as User;
                }
            }

            return user;
        }

        /// <summary>
        /// Function which allows to get all the connected users.
        /// </summary>
        /// <returns></returns>
        public static List<User> GetConnectedUsers()
        {
            List<User> utcs = new List<User>();
            using (cpeinEntities db = new cpeinEntities())
            {
                var results = from UserToConnexion in db.UserToConnexion
                              where UserToConnexion.utc_isdeleted == false
                                 && UserToConnexion.utc_date_logout == null
                              select UserToConnexion;
                foreach (UserToConnexion tmp in results)
                    utcs.Add(tmp.User);
            }

            return utcs;
        }

        /// <summary>
        /// Allows to disconnect the specified user.
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public static bool DiconnectUser(User user)
        {
            if (user == null)
                return false;
            else
            {
                return BUSINESS.BUUserToConnexion.CloseConnexion(user);
            }
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public static List<User> GetRelations(User user)
        {
            {
                if (user == null)
                    return null;
                else
                {
                    using (cpeinEntities db = new cpeinEntities())
                    {

                        // Get all current user's relations
                        var usrs1 = ((from usrRel in db.UserToRelation
                                      join usr in db.User on usrRel.usr_id2 equals usr.usr_id
                                      where (usrRel.usr_id1 == user.usr_id)
                                      select usr));
                        var usrs2   = ((from usrRel in db.UserToRelation
                                      join usr in db.User on usrRel.usr_id1 equals usr.usr_id
                                      where (usrRel.usr_id2 == user.usr_id)
                                      select usr));

                        List<User> lstUser1 = new List<User>(usrs1);
                        List<User> lstUser2 = new List<User>(usrs2);

                        lstUser1.AddRange(lstUser2);
                        return lstUser1;
                    }
                }
            }
        }

        public static List<User> GetPotentialRelations(User user)
        {
            {
                if (user == null)
                    return null;
                else
                {
                    using (cpeinEntities db = new cpeinEntities())
                    {

                        // Get all current user's relations
                        List<User> lstUser1 = new List<User>();
                        List<User> lstUser2 = new List<User>();
                        List<User> lstUser3 = new List<User>();
                        List<User> lstUser4 = new List<User>();
                        lstUser1 = GetRelations(user);
                        foreach (User usr in lstUser1) {
                            lstUser2.AddRange(GetRelations(usr));
                        }
                        lstUser3 = (from usr1 in lstUser2
                                   where usr1.usr_id != user.usr_id &&
                                   !(from u in lstUser1
                                        where u.usr_id != user.usr_id
                                        select u.usr_id).Contains(usr1.usr_id)
                                        select usr1).Distinct(new UserComparer()).ToList();

                        return lstUser3;
                    }
                }
            }
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public static List<Skill> GetSkills(User user)
        {
            if (user == null)
                return null;

            using (cpeinEntities db = new cpeinEntities())
            {
                var skills = ((from usrRel in db.UserToSkill
                                join skl in db.Skill on usrRel.skl_id equals skl.skl_id
                                where (usrRel.usr_id == user.usr_id)
                                select skl));

                List<Skill> lstSkill = new List<Skill>(skills);
                return lstSkill;
            }
        }


        /// <summary>
        /// Function used to get the user news feed.
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public static List<Message> GetNewsFeeds(User user)
        {
            if (user == null)
                return null;
            else
            {
                using (cpeinEntities db = new cpeinEntities())
                {

                    // Get all current user's relations


                    //var msgs = from msg in db.Message
                    //           join usr in db.UserToRelation
                    //           on msg.usr_sender_id equals usr.usr_id2
                    //           where ((usr.usr_id1 == user.usr_id || usr.usr_id2 == user.usr_id) &&
                    //                   usr.utr_approved == true) &&
                    //                 (msg.msg_isdeleted == false && msg.msg_isprivate == false)
                    //           select msg;

                    //get all message from usr_id1 OR usr_id2 (UserToRelation)

                    var msgs = from msg in db.Message
                               where 
                                      ((from usr in db.UserToRelation 
                                      where (usr.usr_id1 == user.usr_id || usr.usr_id2 == user.usr_id) &&
                                              usr.utr_approved == true
                                      select usr.usr_id1).Contains(msg.usr_sender_id) ||
                                      (from usr in db.UserToRelation 
                                      where (usr.usr_id1 == user.usr_id || usr.usr_id2 == user.usr_id) &&
                                              usr.utr_approved == true
                                      select usr.usr_id2).Contains(msg.usr_sender_id) ||
                                      msg.usr_sender_id == user.usr_id) &&
                                      (msg.msg_isdeleted == false && msg.msg_isprivate == false)
                               select msg;

                    return new List<Message>(msgs);
                }
            }
        }

        /// <summary>
        /// Function used to get groups of the user.
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public static List<Group> GetGroups(User user)
        {
            if (user == null)
                return null;
            else
            {
                using (cpeinEntities db = new cpeinEntities())
                {
                    var grps = from g in db.Group
                               join utg in db.UserToGroup on g.grp_id equals utg.grp_id
                               where g.grp_isdeleted == false
                                  && utg.usr_id == user.usr_id
                                  && utg.utg_isdeleted == false
                               select g;
                    return new List<Group>(grps);
                }
            }
        }

        /// <summary>
        /// Function which try to connect a user with a login and a password.
        /// </summary>
        /// <param name="login"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public static User ConnectUser(string login, string password)
        {
            // Try to get the user
            using (cpeinEntities db = new cpeinEntities())
            {
                User user = null;
                var results = from User in db.User
                              where User.usr_login == login
                                 && User.usr_password == password
                              select User;
                foreach (var tmp in results)
                    user = tmp;
                if (user != null)
                {
                    var ids = from User in GetConnectedUsers()
                              select User.usr_id;
                    if (ids.Contains(user.usr_id))
                        return user;
                    else
                    {
                        // We connect the user
                        BUSINESS.BUUserToConnexion.AddUserToConnexion(
                            new UserToConnexion()
                            {
                                usr_id = user.usr_id,
                                utc_date_connection = DateTime.Now,
                                utc_date_logout = null,
                                utc_isdeleted = false,
                            }
                        );

                        return user;
                    }
                }

                return null;
            }
        }
    }
}
